package com.lz.ovuola.service.impl;

import java.math.BigDecimal;
import java.time.temporal.ChronoUnit;
import java.util.Date;
import java.util.List;

import org.apache.commons.collections.CollectionUtils;
import org.springframework.stereotype.Component;

import com.lz.ovuola.util.date.DateUtils;

/**
 * 对temperature hormoneLevels waterContent waterContentDescription
 * physiologicalBalance 等公式计算 公式并不是固定的，后期可能会增加或修改
 * 
 * @see DateUtils
 * @author fz
 *
 */
@Component("physiologicalDataFormula")
public class PhysiologicalDataFormula {

	/**
	 * 天/单位
	 */
	private final Integer PHY_DAYS_FACTOR = 14;
	/**
	 * 小时/单位
	 */
	private final Integer PHY_HOURS_FACTOR = 24;
	private final String LOW = "低";
	private final String MEDIUM = "中";
	private final String HIGH = "高";

	private final String PHY_MEN_STAGE = "例假期";
	private final String PHY_FOL_STAGE = "卵泡成熟期";
	private final String PHY_LUT_STAGE = "黄体期";
	private final String PHY_OVU_STAGE = "排卵日";

	private final String ES_OVU_TIME_MEN_OR_FOL_DES = "天后排卵";
	private final String ES_OVU_TIME_LUT_DES = "已排卵";
	private final String ES_OVU_TIME_OVU_DES = "点排卵";

	/**
	 * waterContent计算
	 * 
	 * @param ch1rh
	 * @return
	 */
	float getWaterContent(float ch1rh) {
		return rounding(ch1rh);
	}

	/**
	 * 通过公式A，获的预估排卵日/公式5A（=本周期例假开始时间+生理周期长度-14天）
	 * 
	 * @param physiologicalLength
	 * @param startTime
	 * @return
	 */
	Date getEstimateOvuTimeByFormulaA(Integer physiologicalLength,
			Date startTime) {
		return DateUtils.getAfterSeveralUnits(startTime, physiologicalLength
				- PHY_DAYS_FACTOR, ChronoUnit.DAYS);
	}

	/**
	 * 通过公式A 获得预估下次例假时间/8A（=本周期例假开始时间+生理周期长度）
	 * 
	 * @param physiologicalLength
	 * @param startTime
	 * @return
	 */
	Date getEstimateStartTimeByFormulaA(Integer physiologicalLength,
			Date startTime) {
		return DateUtils.getAfterSeveralUnits(startTime, physiologicalLength,
				ChronoUnit.DAYS);

	}

	/**
	 * 通过公式B，获的预估排卵日 /5B（=testTime+24小时）
	 * 
	 * @param detectionTime
	 * @return
	 */
	Date getEstimateOvuTimeByFormulaB(Date detectionTime) {
		return DateUtils.getAfterSeveralUnits(detectionTime, PHY_HOURS_FACTOR,
				ChronoUnit.HOURS);

	}

	/**
	 * 通过公式B 获得预估下次例假时间 /公式8B（=排卵日+14天）
	 * 
	 * @param estimateOvulationTime
	 * @return
	 */
	Date getEstimateStartTimeByFormulaB(Date estimateOvulationTime) {
		return DateUtils.getAfterSeveralUnits(estimateOvulationTime,
				PHY_DAYS_FACTOR, ChronoUnit.DAYS);
	}

	/**
	 * 
	 * 易孕指数 if，排卵日+1>=本日>=排卵日-2，为高（共4天） if，本日=排卵日+2，or本日=排卵日-3，为中（2天） if else，为低
	 * 
	 * @param detectionTime
	 * @param estimateOvulationTime
	 * @return
	 */
	String getPregnancyRateDescription(Date detectionTime,
			Date estimateOvulationTime) {
		String pregnancyRateDescription = "";
		Date highStartTime = DateUtils.getAfterSeveralUnits(
				estimateOvulationTime, -2, ChronoUnit.DAYS);
		Date highEndTime = DateUtils.getAfterSeveralUnits(
				estimateOvulationTime, 1, ChronoUnit.DAYS);
		Date mediumStartTime = DateUtils.getAfterSeveralUnits(
				estimateOvulationTime, -3, ChronoUnit.DAYS);
		Date mediumEndTime = DateUtils.getAfterSeveralUnits(
				estimateOvulationTime, 2, ChronoUnit.DAYS);

		if ((DateUtils.gt(detectionTime, highStartTime) && DateUtils.gt(
				highEndTime, detectionTime))
				|| DateUtils.eq(detectionTime, highStartTime)
				|| DateUtils.eq(highEndTime, detectionTime)) {
			pregnancyRateDescription = HIGH;
		} else if (DateUtils.gt(detectionTime, mediumEndTime)
				|| DateUtils.gt(mediumStartTime, detectionTime)) {
			pregnancyRateDescription = LOW;
		} else {
			pregnancyRateDescription = MEDIUM;
		}
		return pregnancyRateDescription;
	}

	/**
	 * if，上传为例假日，=例假期 if，排卵日期>本次检测时间>例假结束日期，=卵泡成熟期 if，本次检测日期>排卵日，=黄体期
	 * if，本次检测日期=排卵日，=排卵日
	 * 
	 * @param endTime
	 * @param detectionTime
	 * @param estimateOvulationTime
	 * @return
	 */
	String getPhysiologicalStage(Date endTime, Date detectionTime,
			Date estimateOvulationTime) {
		String phyStage = "";
		if (DateUtils.dateToLocalDate(detectionTime).isBefore(
				DateUtils.dateToLocalDate(endTime))
				|| DateUtils.dateToLocalDate(detectionTime).equals(
						DateUtils.dateToLocalDate(endTime))) {
			phyStage = PHY_MEN_STAGE;
		} else if (DateUtils.dateToLocalDate(estimateOvulationTime).isAfter(
				DateUtils.dateToLocalDate(detectionTime))) {
			phyStage = PHY_FOL_STAGE;
		} else if (DateUtils.dateToLocalDate(detectionTime).isAfter(
				DateUtils.dateToLocalDate(estimateOvulationTime))) {
			phyStage = PHY_LUT_STAGE;
		} else {
			phyStage = PHY_OVU_STAGE;
		}
		return phyStage;
	}

	/**
	 * 预估排卵日描述 1、例假期和卵泡成熟期：多少天后排卵 2、排卵日 ：几点排卵 3、黄体期：已排卵
	 * 
	 * @param estimateOvulationTime
	 * @param phyStage
	 * @return
	 */
	String getEovtDescription(Date estimateOvulationTime, String phyStage,
			Date detectionTime) {
		String esOvtimeDesc = "";
		if (phyStage.equals(PHY_MEN_STAGE) || phyStage.equals(PHY_FOL_STAGE)) {
			esOvtimeDesc = DateUtils.calDateToDay(detectionTime,
					estimateOvulationTime) + ES_OVU_TIME_MEN_OR_FOL_DES;
		} else if (phyStage.equals(PHY_LUT_STAGE)) {
			esOvtimeDesc = ES_OVU_TIME_LUT_DES;
		} else {
			esOvtimeDesc = DateUtils.dateToLocalDateTime(estimateOvulationTime)
					.getHour() + ES_OVU_TIME_OVU_DES;
		}
		return esOvtimeDesc;
	}

	/**
	 * 是否是例假
	 * 
	 * @param phyStage
	 * @return
	 */
	Integer isMen(String phyStage) {
		if (phyStage.equals(PHY_MEN_STAGE))
			return 1;
		else
			return 2;
	}

	/**
	 * 保留小数点后两位，四舍五入
	 * 
	 * @param f
	 * @return
	 */
	private float rounding(float f) {
		BigDecimal b = new BigDecimal(f);
		return b.setScale(2, BigDecimal.ROUND_HALF_UP).floatValue();
	}

	/**
	 * 求： 期望(平均值)
	 * 
	 * @param waterContents
	 * @return
	 */
	float getMean(List<Float> waterContents) {
		if (CollectionUtils.isEmpty(waterContents))
			return 0.00F;
		Float sum = 0.00F;
		for (Float waterContent : waterContents) {
			sum = Float.sum(sum, waterContent);
		}
		return sum / waterContents.size();
	}

	/**
	 * 求标准差
	 * 
	 * @param waterContents
	 * @param mean
	 * @return
	 */
	float getStandardDeviation(List<Float> waterContents, Float mean) {
		Float sum = 0.00F;
		for (Float waterContent : waterContents) {
			Float temp = waterContent - mean;
			sum = Float.sum(sum, temp * temp);
		}
		return rounding((float) (Math.sqrt(sum) / waterContents.size()));
	}

	// public static void main(String[] args) {
	// List<Float> waterContents = new ArrayList<Float>();
	// // waterContents.add(4.00F);
	// // waterContents.add(5.00F);
	// // waterContents.add(2.00F);
	// // waterContents.add(7.00F);
	//
	// waterContents.add(1.00F);
	//
	// Collections.sort(waterContents,
	// (first, second) -> first.compareTo(second));
	// for (Float float1 : waterContents) {
	// System.out.println(float1);
	//
	// }
	//
	// System.out.println(waterContents.get(waterContents.size() / 2));
	// }

	public Integer getPHY_DAYS_FACTOR() {
		return PHY_DAYS_FACTOR;
	}

	public Integer getPHY_HOURS_FACTOR() {
		return PHY_HOURS_FACTOR;
	}

}
